home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (C) 1994, Silicon Graphics, Inc.
- * All Rights Reserved.
- *
- * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
- * the contents of this file may not be disclosed to third parties, copied or
- * duplicated in any form, in whole or in part, without the prior written
- * permission of Silicon Graphics, Inc.
- *
- * RESTRICTED RIGHTS LEGEND:
- * Use, duplication or disclosure by the Government is subject to restrictions
- * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
- * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
- * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
- * rights reserved under the Copyright Laws of the United States.
- */
- /*
- Front-end to malloc that does counting and stuff.
- Expects there to be a "real" malloc and free and realloc.
- */
-
- #include "dmalloc.h"
- #include "stacktrace.h"
- #include <sys/types.h>
- #include <malloc.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <bstring.h>
- #include <sys/param.h>
- #include <assert.h>
- #include <unistd.h>
- #include <stdarg.h>
- #include <fcntl.h>
- #include <ctype.h>
-
- /*
- sproc creates three spinlocks: __malloclock, __monlock, and __randlock.
- We can't use __malloclock because we call the real malloc which uses it,
- and that would cause deadlock.
- */
- static pid_t lock_owner;/* XXXassume shared--this may not be a good assumption*/
- static int lock_depth; /* number of times this process holds it */
- #define THELOCK __randlock
- extern ulock_t THELOCK;
- #ifdef LOCK_DEBUG
- #define lock_debug getenv("MALLOC_LOCK_DEBUG")
- #else
- #define lock_debug 0
- #endif
- static void
- LOCK_MALLOC(int inc)
- {
- if (lock_debug)
- printf("%d(%d): LOCKING %d+%d (owner=%d)\n", get_pid(), getpid(), lock_depth,inc,lock_owner);
- if (!THELOCK) {
- lock_depth += inc;
- if (lock_debug)
- printf("%d(%d): locked %d\n", get_pid(), getpid(), lock_depth);
- return; /* no sprocs occured yet */
- }
-
- if (lock_owner != get_pid()) { /* someone else has it, or no one does */
- ussetlock(THELOCK);
- lock_owner = get_pid();
- assert(lock_depth == 0);
- } else { /* this process already has it */
- assert(lock_depth > 0);
- }
-
- lock_depth += inc; /* we have the lock */
- if (lock_debug)
- printf("%d(%d): LOCKED %d\n", get_pid(), getpid(), lock_depth);
- }
- static void
- UNLOCK_MALLOC(int inc)
- {
- if (lock_debug)
- printf("%d(%d): UNLOCKING %d-%d (owner=%d)\n", get_pid(), getpid(), lock_depth,inc,lock_owner);
- if (!THELOCK) {
- lock_depth -= inc;
- if (lock_debug)
- printf("%d(%d): unlocked %d\n", get_pid(), getpid(), lock_depth);
- return; /* no sprocs occured yet */
- }
-
- assert(lock_owner == get_pid());
- assert(lock_depth > 0);
-
- if ((lock_depth -= inc) == 0) {
- lock_owner = 0;
- usunsetlock(THELOCK);
- }
- if (lock_debug)
- printf("%d(%d): UNLOCKED %d\n", get_pid(), getpid(), lock_depth);
- }
-
- #define CREATE_MALLOC_LOCK() 0 /* not needed, since we use monlock */
-
-
- #undef MIN
- #define MIN(a,b) ((a)<(b)?(a):(b))
- #undef MAX
- #define MAX(a,b) ((a)>(b)?(a):(b))
- #define INRANGE(foo,bar,baz) ((foo(bar))&&((bar)baz))
- #define streq(s,t) (strcmp(s, t) == 0)
-
- #ifndef real_malloc
- #define real_malloc mAlLoC
- #endif
- #ifndef real_free
- #define real_free fReE
- #endif
- #ifndef real_realloc
- #define real_realloc rEaLlOc
- #endif
- # ifdef __cplusplus
- extern "C" {
- # endif
- extern void *real_malloc(size_t);
- extern void real_free(void *);
- extern void *real_realloc(void *, size_t);
- # ifdef __cplusplus
- };
- # endif
-
- #define type_MALLOC ((void *)0)
- #define type_FREE ((void *)1)
-
- #ifndef MAX_MALLOCS
- #define MAX_MALLOCS 10000 /* max number of occurrances of malloc&free */
- #endif
- #ifndef MAX_STACKTRACE_DEPTH
- #define MAX_STACKTRACE_DEPTH 10
- #endif
- #define MAX_SUM_OF_STACKTRACES (MAX_MALLOCS * MAX_STACKTRACE_DEPTH) /* XXX for now, until we do better bounds checking */
- #define SYMBUFSIZ 1024
-
- struct hist {
- void *type; /* type_MALLOC or type_FREE */
-
- int ncalls, nbytes;
- int ncalls_freed, nbytes_freed; /* if it's a malloc */
-
- int stacktrace_depth;
- void **stacktrace; /* pointer into stacktraces buffer */
- };
-
- /*
- * Stuff that gets prepended and appended to each malloc block.
- * Wow, that's a lot of stuff.
- */
- static struct header {
- int magic0, magic1;
- int n;
- struct hist *hist;
- struct tailer *tail;
- #define LINKED_LIST
- #ifdef LINKED_LIST
- struct header *prev, *next; /* linked list */
- #endif /* LINKED_LIST */
- int pad; /* XXX fudge-- if this is removed, modify list below */
- int magic2, magic3;
- } aheader = { 0xdeadbeef, 0xfedcba98, 0,0,0,
- #ifdef LINKED_LIST
- 0,0,
- #endif /* LINKED_LIST */
- 0, 0x3456789a, 0xabbaabba};
- static struct tailer {
- int magic4, magic5;
- struct header *head;
- int magic6, magic7;
- } atailer = { 0xcacacaca, 0x89898989, 0, 0xface6969, 0x81818181 };
-
- #ifdef LINKED_LIST
- static struct header *firstheader = NULL;
- static void
- link_header(struct header *hdr)
- {
- hdr->next = firstheader;
- if (firstheader)
- firstheader->prev = hdr;
- hdr->prev = NULL;
- firstheader = hdr;
- }
- static void
- unlink_header(struct header *hdr)
- {
- if (hdr->next)
- hdr->next->prev = hdr->prev;
- if (hdr->prev)
- hdr->prev->next = hdr->next;
- else
- firstheader = hdr->next;
- }
- #endif /* LINKED_LIST */
-
- static struct header *lowest_head_ever = NULL, *highest_head_ever = NULL;
-
- /* big static variables */
- static struct hist hists[MAX_MALLOCS];
- static int nhists = 0;
- static void *hists_stacktrace_buffer[MAX_SUM_OF_STACKTRACES];
- static int hists_stacktrace_buffer_size = 0;
-
- static int ncalls_malloc = 0;
- static int ncalls_free = 0;
- static int nbytes_malloced = 0;
- static int nbytes_freed = 0;
-
- static int ncalls_malloc_really = 0; /* Not touched by reset */
- static int ncalls_free_really = 0; /* Not touched by reset */
- static int nbytes_malloced_really = 0; /* Not touched by reset */
- static int nbytes_freed_really = 0; /* Not touched by reset */
- static int max_diff_bytes = 0; /* Not touched by reset */
-
- static void *malloc_block_of_interest = NULL;
- static int malloc_call_of_interest = -1;
- static int depth_of_trace_of_interest = -1;
- static void *trace_of_interest[MAX_STACKTRACE_DEPTH];
-
- #define MALLOC_FILL '\001'
- #define FREE_FILL '\002'
-
- static void
- fdprintf(int fd, char *fmt, ...)
- {
- char buf[MAXPATHLEN*2];
- va_list args;
-
- va_start(args, fmt);
-
- vsprintf(buf, fmt, args);
- write(fd, buf, strlen(buf));
-
- va_end(args);
- }
-
-
- /* external parameters user can mess with -- maybe should be mallopt option */
- int malloc_stacktrace_get_depth = 0; /* default is don't trace */
- int malloc_fillarea = 1; /* fill area by default */
- extern void malloc_init_function();
-
- static void
- _atexit()
- {
- int do_info = 0, do_free = 0, do_check = 1; /* note default values */
- char *p;
-
- if (p = getenv("MALLOC_INFO_ATEXIT"))
- do_info = (*p ? atoi(p) : 1);
- if (p = getenv("FREE_ATEXIT"))
- do_free = (*p ? atoi(p) : 1);
- if (p = getenv("MALLOC_CHECK_ATEXIT"))
- do_check = (*p ? atoi(p) : 1);
-
- if (do_info)
- malloc_info(0, -1);
-
- #ifdef LINKED_LIST
- if (do_free) {
- if (do_free >= 2) {
- fdprintf(2, "%s(%d): Freeing everything...",
- stacktrace_get_argv0() ? stacktrace_get_argv0() : "",
- getpid());
- }
-
- LOCK_MALLOC(1);
- while (firstheader)
- free((void *)(firstheader+1));
- UNLOCK_MALLOC(1);
-
- if (do_free >= 2)
- fdprintf(2, "done.\n");
- }
-
- if (do_check) {
- if (do_check >= 2) {
- fdprintf(2, "%s(%d): Checking malloc chain at exit...",
- stacktrace_get_argv0() ? stacktrace_get_argv0() : "",
- getpid());
- }
-
- malloc_check_during("exit");
-
- if (do_check >= 2)
- fdprintf(2, "done.\n");
- }
- #endif /* LINKED_LIST */
- }
-
- #ifdef LINKED_LIST
- extern int
- malloc_check_during(char *during)
- {
- int is_bad = 0;
- struct header *p;
-
- LOCK_MALLOC(1);
- for (p = firstheader; p; p = p->next)
- if (!malloc_isgoodblock_during(p+1, during))
- is_bad = 1;
- UNLOCK_MALLOC(1);
-
- return is_bad ? -1 : 0;
- }
- extern int
- malloc_check()
- {
- return malloc_check_during("malloc_check");
- }
- #endif /* LINKED_LIST */
-
- static void
- init_from_env()
- {
- static int called_already = 0;
- if (!called_already) {
- char *p;
-
- /* try to snarf argv[0] as soon as possible, in case main clobbers it */
- (void)stacktrace_get_argv0();
-
- if (p = getenv("MALLOC_FILLAREA"))
- malloc_fillarea = (*p ? atoi(p) : 1);
- if (p = getenv("MALLOC_STACKTRACE_GET_DEPTH"))
- malloc_stacktrace_get_depth = (*p ? atoi(p) : 0);
-
- /* XXX it might be useful to make a nice callback interface... */
- /* XXX and definitely want a gui */
- if (p = getenv("MALLOC_STACKTRACE_OF_INTEREST")) {
- depth_of_trace_of_interest =
- sscanf(p, "%x %x %x %x %x %x %x %x %x %x",
- &trace_of_interest[0],
- &trace_of_interest[1],
- &trace_of_interest[2],
- &trace_of_interest[3],
- &trace_of_interest[4],
- &trace_of_interest[5],
- &trace_of_interest[6],
- &trace_of_interest[7],
- &trace_of_interest[8],
- &trace_of_interest[9]);
- if (depth_of_trace_of_interest == -1)
- depth_of_trace_of_interest = 0; /* dumbass sscanf */
- }
- /*
- * Note: we must have some code in this file that
- * sets the value of malloc_call_of_interest,
- * otherwise we will not be able to set it properly in the debugger
- * (due to compiler optimization or something).
- */
- /* XXX want a gui for this */
- if (p = getenv("MALLOC_CALL_OF_INTEREST"))
- malloc_call_of_interest = (int) strtoul(p, (char **)NULL, 0);
- if (p = getenv("MALLOC_BLOCK_OF_INTEREST"))
- malloc_block_of_interest = (void *)strtoul(p, (char **)NULL, 0);
-
- if (p = getenv("MALLOC_PROMPT_ON_STARTUP")) {
- if (!*p || streq(p, stacktrace_get_argv0())) {
- int tty = open("/dev/tty", 2);
- char c;
- assert(tty >= 0);
- fdprintf(tty, "%s(%d): hit return to continue",
- stacktrace_get_argv0(), getpid());
- read(tty, &c, 1);
- close(tty);
- }
- }
-
-
- malloc_init_function(); /* call application-defined function */
-
- atexit(_atexit);
-
- called_already = 1;
- /*
- called_already must be true at this point,
- so that this block won't get re-entered
- if the us_ routines call malloc.
- */
- CREATE_MALLOC_LOCK();
-
- }
- }
-
-
- #define MALLOC_STACKTRACE_GET_DEPTH (malloc_stacktrace_get_depth == -1 ? MAX_STACKTRACE_DEPTH : MIN(malloc_stacktrace_get_depth, MAX_STACKTRACE_DEPTH))
-
- /* only compares up to the min of the two sizes */
- static int
- stacktracecmp(int siz0, void *trace0[], int siz1, void *trace1[])
- {
- int i;
- for (i = 0; i < siz0 && i < siz1; ++i)
- if (((unsigned long *)trace0)[i] != ((unsigned long *)trace1)[i]) {
- if (((unsigned long *)trace0)[i] < ((unsigned long *)trace1)[i])
- return -1;
- else
- return 1;
- }
- return siz0 - siz1;
- }
-
- static int
- stacktracencmp(int siz0, void *trace0[], int siz1, void *trace1[], int n)
- {
- int i;
- for (i = 0; i < siz0 && i < siz1 && i < n; ++i)
- if (((unsigned long *)trace0)[i] != ((unsigned long *)trace1)[i]) {
- if (((unsigned long *)trace0)[i] < ((unsigned long *)trace1)[i])
- return -1;
- else
- return 1;
- }
- if (i == n)
- return 0;
- return siz0 - siz1;
- }
-
- /* simple prime testing-- we only need it to initialize hashsiz */
- static int
- isprime(int n)
- {
- int i;
- if (n % 2 == 0)
- return 0;
- for (i = 3; i*i <= n; ++i)
- if (n % i == 0)
- return 0;
- return 1;
- }
-
- static int
- hashfun(int depth, void *trace[], int hashsiz)
- {
- int i; /* not unsigned! or (i < depth) will be true when i==0 and depth<0 */
- unsigned long sum = 0;
- for (i = 0; i < depth; ++i)
- sum += (((unsigned long)trace[i])/4);
- return sum % (hashsiz-1) + 1; /* in range 1..hashsiz-1 */
- }
-
- #ifdef HASH_STATS
- static int n_collisions = 0;
- static int n_distinct_collisions = 0;
- static int n_pileups;
- static int n_distinct_pileups;
- static int max_pileup = 0;
- extern void
- _malloc_history_print_stats()
- {
- printf("%d max pileup\n", max_pileup);
- printf("%d distinct pileups\n", n_distinct_pileups);
- printf("%d total pileups\n", n_pileups);
- printf("%d distinct collisions\n", n_distinct_collisions);
- printf("%d total collisions\n", n_collisions);
- }
- #endif
-
- static struct hist *
- find_existing_hist(void *type, int stacktrace_depth, void *stacktrace[])
- {
- #ifndef HASHSIZ
- #define HASHSIZ (MAX_MALLOCS * 3/2)
- #endif
- static struct hist *hashtable[HASHSIZ];
- static int hashsiz = 0;
- int i, hash;
- #ifdef HASH_STATS
- int pileup = 0;
- #endif
-
- /*
- * Make the size of the hash table prime so that
- * for every hash, 0 < hash < hashsiz, the sequence
- * hash, 2*hash, 3*hash, ... (mod hashsiz)
- * is a unique path through 1..hashsize-1 (this allows a simple
- * rehashing mechanism that avoids pileups).
- * Note that 0 is not allowed-- make sure hashfun() can not return 0!
- * (hashtable[0] is not used, but subtracting 1 from everything is
- * not worth the trouble).
- */
- if (!hashsiz) /* then hashsiz hasn't been initialized yet */
- for (hashsiz = HASHSIZ; !isprime(hashsiz); hashsiz--)
- ;
-
- hash = hashfun(stacktrace_depth, stacktrace, hashsiz);
- for (i = hash; hashtable[i];
- #ifdef HASH_STUPID
- i++
- #else
- i = (i+hash) % hashsiz
- #endif
- ) {
- if (!stacktracecmp(stacktrace_depth, stacktrace,
- hashtable[i]->stacktrace_depth,
- hashtable[i]->stacktrace)) {
- return hashtable[i];
- }
- #ifdef HASH_STATS
- pileup++;
- n_collisions++;
- n_pileups += (pileup == 1);
- #endif
- }
- if (nhists == MAX_MALLOCS)
- return NULL;
-
- #ifdef HASH_STATS
- n_distinct_collisions += pileup;
- n_distinct_pileups += (pileup > 0);
- max_pileup = MAX(pileup, max_pileup);
- #endif
-
- hashtable[i] = &hists[nhists];
- return hashtable[i];
- }
-
- /* just places for the debugger to stop... */
- extern void
- malloc_of_interest()
- {
- printf(""); /* make sure this function doesn't get optimized out */
- }
-
- static struct hist *
- findhist(void *type, int stacktrace_depth, void *stacktrace[])
- {
- int i;
- struct hist *h;
-
- h = find_existing_hist(type, stacktrace_depth, stacktrace);
-
- if (h == NULL) {
- static int already_complained = 0;
- if (!already_complained) {
- fdprintf(2, "Turning off malloc tracing: more than %d distinct mallocs & frees\n", MAX_MALLOCS);
- already_complained = 1;
- }
- return NULL;
- }
-
- if (depth_of_trace_of_interest >= 0
- && !stacktracecmp(stacktrace_depth, stacktrace,
- depth_of_trace_of_interest, trace_of_interest)) {
- malloc_of_interest();
- /* make each one separate XXXthis should go with other bounds checking*/
- if (nhists < MAX_MALLOCS) {
- h = hists+nhists;
- }
- }
-
- if (h-hists == nhists) { /* then it wasn't really existing */
- nhists++;
-
- h->type = type;
- h->ncalls = 0;
- h->nbytes = 0;
- h->ncalls_freed = 0;
- h->nbytes_freed = 0;
-
- /* XXX need to do some bounds checking here */
- h->stacktrace_depth = MIN(stacktrace_depth, MAX_STACKTRACE_DEPTH);
- h->stacktrace = hists_stacktrace_buffer + hists_stacktrace_buffer_size;
- hists_stacktrace_buffer_size += h->stacktrace_depth;
- for (i = 0; i < h->stacktrace_depth; ++i)
- h->stacktrace[i] = stacktrace[i];
- }
-
- return h;
- }
-
- /*
- * Stuff to do on each malloc or realloc
- */
- static void
- malloc_do_stuff(size_t n, struct header *head)
- {
- struct hist *h;
- int stacktrace_depth;
- void *stacktrace[MAX_STACKTRACE_DEPTH];
-
- if (malloc_block_of_interest == (void *)(head+1))
- malloc_of_interest();
- if (malloc_call_of_interest == ncalls_malloc)
- malloc_of_interest();
-
- if (!lowest_head_ever || head < lowest_head_ever)
- lowest_head_ever = head;
- if (!highest_head_ever || head > highest_head_ever)
- highest_head_ever = head;
-
- ncalls_malloc++;
- ncalls_malloc_really++;
- nbytes_malloced += n;
- nbytes_malloced_really += n;
-
- if (nbytes_malloced_really - nbytes_freed_really > max_diff_bytes)
- max_diff_bytes = nbytes_malloced_really - nbytes_freed_really;
-
- /* don't get any traces in recursive mallocs */
- if (lock_depth >= 200)
- stacktrace_depth = 0;
- else
- stacktrace_depth = stacktrace_get(2, MALLOC_STACKTRACE_GET_DEPTH,
- stacktrace);
-
- h = findhist(type_MALLOC, stacktrace_depth, stacktrace);
- if (h) {
- h->ncalls++;
- h->nbytes += n;
- }
-
- head->n = n;
- head->hist = h;
-
- #ifdef LINKED_LIST
- link_header(head);
- #endif
- }
-
- static int
- strcontains(char *S, char *s) /* XXX remove this when no longer used */
- {
- if (!S || !s)
- return 0;
- for (; *S; S++)
- if (!strncmp(S, s, strlen(s)))
- return 1;
- return 0;
- }
-
- /*
- Are we suppressing messages about this block
- because of an environment variable?
- */
- static int
- suppressing(void *block)
- {
- char *p = getenv("MALLOC_SUPPRESS");
- if (!p)
- return 0;
-
- while (p && *p) {
- char argv0_to_suppress[4096];
- long block_to_suppress = 0;
- while (*p && isspace(*p))
- p++;
- if (sscanf(p, "%[^:]:%lx", argv0_to_suppress, &block_to_suppress) != 2){
- argv0_to_suppress[0] = '\0';
- if (sscanf(p, "%lx", &block_to_suppress) != 1)
- break;
- }
-
- if (streq(argv0_to_suppress, stacktrace_get_argv0())
- && block_to_suppress == (long)block)
- return 1; /* found it; suppress it */
- while (*p && !isspace(*p))
- p++;
- while (*p && isspace(*p))
- p++;
- }
- return 0; /* not found in suppress list; don't suppress */
- }
-
- extern void /* can stop here for breakpoint debugging */
- malloc_bad(void *block)
- {
- printf(""); /* make sure it doesn't get optimized out */
- }
-
- extern int
- malloc_isgoodblock_during(void *block, char *during)/* XXX think of a better name */
- {
- int its_bad = 0;
- struct header *head = ((struct header *)block) - 1;
- struct tailer *tail;
- int nwrong_in_head, nwrong_in_tail;
- char *p;
-
- /*
- * Sanity check so we don't buserror ourself
- */
- if (((long)head & 3L) ||
- head < lowest_head_ever || head > highest_head_ever) {
- fdprintf(2, "%s(%d): WARNING: %#x is not a valid malloced block,\n\tdetected during %s\n",
- stacktrace_get_argv0() ? stacktrace_get_argv0() : "",
- getpid(),
- head+1, during);
- malloc_bad(block);
- return 0; /* failure */
- }
-
- /*
- * Heavy duty bounds checking
- */
- nwrong_in_head = (head->magic0 != aheader.magic0)
- + (head->magic1 != aheader.magic1)
- + (head->magic2 != aheader.magic2)
- + (head->magic3 != aheader.magic3);
- if (nwrong_in_head > 0) {
- if (!suppressing(head+1))
- fdprintf(2,
- "%s(%d): ERROR: underflow corruption detected during %s\n\tat malloc block %#x\n",
- stacktrace_get_argv0() ? stacktrace_get_argv0() : "",
- getpid(),
- during,
- head+1);
- its_bad = 1;
- }
- tail = head->tail;
- nwrong_in_tail = (tail->magic4 != atailer.magic4)
- + (tail->magic5 != atailer.magic5)
- + (tail->head != head)
- + (tail->magic6 != atailer.magic6)
- + (tail->magic7 != atailer.magic7);
- /* check that the < 4 bytes of extra alignment space are
- still filled with MALLOC_FILL */
- for (p = (char *)(head+1) + head->n; p < (char *)tail; ++p)
- if (*p != MALLOC_FILL)
- nwrong_in_tail++;
-
- if (nwrong_in_tail > 0) {
- if (!suppressing(head+1))
- fdprintf(2, "%s(%d): ERROR: overflow corruption detected during %s\n\tat malloc block %#x (%s%d bytes)\n",
- stacktrace_get_argv0() ? stacktrace_get_argv0() : "",
- getpid(),
- during,
- head+1,
- nwrong_in_head ? "maybe " : "",
- head->n);
- its_bad = 1;
- }
- if (!nwrong_in_head && !nwrong_in_tail && ((long)head->hist & 1)) {
- /*
- * When we free the block, we set the loworder bit of hist.
- * Of course the real free and malloc may clobber it,
- * but if that happened, the above never would have passed.
- */
- if (!suppressing(head+1))
- fdprintf(2, "WARNING: %#x (%d bytes) has been freed already,\n\tdetected during %s\n",
- head+1, head->n, during);
- its_bad = 1;
- }
-
- if (its_bad) {
- char buf[100];
- /* snarf the stacktrace away; since the block has been freed
- already, our printing may clobber it */
- int stacktrace_depth;
- void *stacktrace[MAX_STACKTRACE_DEPTH];
- stacktrace_depth = ((struct hist *)((long)head->hist&~1))->stacktrace_depth;
- bcopy(((struct hist *)((long)head->hist&~1))->stacktrace, stacktrace,
- stacktrace_depth * sizeof(*stacktrace));
-
- malloc_bad(block);
-
- /*
- XXX Quick semi-accurate attempt to see whether we are in a DSO--,
- since we will probably core dump if we are and the main
- program is stripped and we try to get a stack trace.
- */
- if (!suppressing(head+1) &&
- (strcontains(getenv("_RLD_LIST"), "libdmalloc")
- && getenv("_MALLOC_TRY_TO_PRINT_STACKTRACES")
- ||!strcontains(getenv("_RLD_LIST"), "libdmalloc")
- && !getenv("_MALLOC_DONT_TRY_TO_PRINT_STACKTRACES"))) {
-
- simple_stacktrace_print(/*fd*/2, NULL, /*skip*/3, 100);
- }
-
- if (!suppressing(head+1) &&
- (strcontains(getenv("_RLD_LIST"), "libdmalloc")
- && getenv("_MALLOC_TRY_TO_PRINT_STACKTRACES")
- ||!strcontains(getenv("_RLD_LIST"), "libdmalloc")
- && !getenv("_MALLOC_DONT_TRY_TO_PRINT_STACKTRACES"))) {
- fdprintf(2, "This block may have been allocated here:\n");
- simple_stacktrace_write(/*fd*/2, /*fmt*/NULL, /*filename*/NULL,
- stacktrace_depth, stacktrace);
- }
-
- return 0; /* failure */
- }
-
- return 1; /* success */
- }
- extern int
- malloc_isgoodblock(void *block) /* XXX think of a better name */
- {
- return malloc_isgoodblock_during(block, "malloc_isgoodblock");
- }
-
-
- /*
- * Stuff to do on each free or realloc.
- * Return 1 on success, 0 if corruption was detected.
- */
- static int
- free_do_stuff(struct header *head, char *during)
- {
- struct hist *h;
- int stacktrace_depth;
- void *stacktrace[MAX_STACKTRACE_DEPTH];
-
- if (!malloc_isgoodblock_during(head+1, during))
- return 0; /* failure */
-
- ncalls_free++;
- ncalls_free_really++;
- nbytes_freed += head->n;
- nbytes_freed_really += head->n;
-
- if (lock_depth >= 200)
- stacktrace_depth = 0;
- else
- stacktrace_depth = stacktrace_get(2, MALLOC_STACKTRACE_GET_DEPTH,
- stacktrace);
-
- h = findhist(type_FREE, stacktrace_depth, stacktrace);
- if (h) {
- h->ncalls++;
- h->nbytes += head->n;
- }
-
- if (head->hist) {
- head->hist->ncalls_freed++;
- head->hist->nbytes_freed += head->n;
- }
-
- #ifdef LINKED_LIST
- unlink_header(head);
- #endif
-
- return 1; /* success */
- }
-
- #define ALIGNUP(n) (((n)+3)&~3)
-
- extern void
- malloc_failed()
- {
- /* this function exists for breakpoint debugging. */
- printf(""); /* make sure this function doesn't get optimized out */
- /* XXX maybe should allow a callback here? */
- }
-
- /*
- * Front end to the real malloc
- */
- extern void *
- malloc(size_t n)
- {
- struct header *head;
-
- init_from_env();
-
- LOCK_MALLOC(100);
-
- head = 0; /* XXX suppress stupid compiler warning */
- head = (struct header *) real_malloc(ALIGNUP(n + sizeof(*head))
- + sizeof(struct tailer));
- if (!head) {
- malloc_failed();
- UNLOCK_MALLOC(100);
- return NULL;
- }
-
- *head = aheader;
- head->tail = (struct tailer *)(((char *)head) + ALIGNUP(n + sizeof(*head)));
- *head->tail = atailer;
- head->tail->head = head;
-
- malloc_do_stuff(n, head);
-
- if (malloc_fillarea)
- memset((void *)(head+1), MALLOC_FILL, n);
-
- /* always fill any space after the buffer and before the tail,
- for overflow detection */
- memset((void *)((char *)(head+1)+n),
- MALLOC_FILL,
- (char *)head->tail - ((char *)(head+1)+n));
-
- UNLOCK_MALLOC(100);
- return (void *)(head+1);
- }
-
- /*
- * Front end to the real free
- */
- extern void
- free(void *p)
- {
- struct header *vom;
-
- init_from_env();
-
- LOCK_MALLOC(100);
-
- if (!p) {
- UNLOCK_MALLOC(100);
- return; /* XXX do we want to save this information? */
- }
- vom = ((struct header *)p) - 1;
-
- if (!free_do_stuff(vom, "free")) {
- UNLOCK_MALLOC(100);
- return; /* free_do_stuff prints its own error message */
- }
-
- if (malloc_fillarea)
- memset((void *)(vom+1), FREE_FILL, vom->n);
-
- vom->hist = (struct hist *)((long)vom->hist | 1);
-
- real_free((void *)vom);
-
- UNLOCK_MALLOC(100);
- }
-
- /*
- * Front end to the real realloc
- */
- extern void *
- realloc(void *p, size_t n)
- {
- struct header *head;
- int old_n;
-
- if (!p)
- return malloc(n); /* this is what the sgi man page says, anyway */
-
- init_from_env();
-
- LOCK_MALLOC(100);
-
- head = ((struct header *)p) - 1;
-
- old_n = head->n;
-
- if (!free_do_stuff(head, "realloc")) {
- UNLOCK_MALLOC(100);
- return NULL; /* free_do_stuff prints its own error message */
- }
-
- if (malloc_fillarea && n < old_n)
- memset((void *)((char *)(head+1) + n), FREE_FILL, old_n - n);
-
- head = (struct header *) real_realloc((void *)head,
- ALIGNUP(n + sizeof(*head)) + sizeof(struct tailer));
-
- if (!head) {
- UNLOCK_MALLOC(100);
- return NULL; /* XXX do we want to save this information? */
- }
-
- head->tail = (struct tailer *)(((char *)head) + ALIGNUP(n+sizeof(*head)));
- *head->tail = atailer;
- head->tail->head = head;
-
- malloc_do_stuff(n, head);
-
- if (malloc_fillarea && n > old_n)
- memset((void *)((char *)(head+1) + old_n), MALLOC_FILL, n - old_n);
-
- /* always fill any space after the buffer and before the tail,
- for overflow detection */
- memset((void *)((char *)(head+1)+n),
- MALLOC_FILL,
- (char *)head->tail - ((char *)(head+1)+n));
-
- UNLOCK_MALLOC(100);
- return (void *)(head + 1);
- }
-
-
- /*
- * Sorting utilities
- */
- static void
- multiqsort(char *base, int n, int elsiz, int (*cmps[])(const void *, const void *))
- /* cmps is a null-terminated array of comparison functions */
- {
- int i, (*cmp)(const void *, const void *);
-
- if (!(cmp = cmps[0]))
- return;
-
- qsort(base, n, elsiz, cmp);
-
- if (cmps[1])
- for (; n > 0; n -= i, base += i*elsiz) {
- for (i = 0; i < n; ++i)
- if ((*cmp)(base, base + i*elsiz))
- break;
- if (i > 1)
- multiqsort(base, i, elsiz, cmps+1);
- }
- }
-
- static int cmp_leaks(struct hist **a, struct hist **b)
- {
- return ((*b)->nbytes - (*b)->nbytes_freed)
- - ((*a)->nbytes - (*a)->nbytes_freed); /* highest first */
- }
-
- static int cmp_ncalls(struct hist **a, struct hist **b)
- {
- return (*b)->ncalls - (*a)->ncalls; /* highest first */
- }
-
- static int cmp_stacktrace(struct hist **a, struct hist **b)
- {
- return stacktracecmp((*a)->stacktrace_depth, (*a)->stacktrace, (*b)->stacktrace_depth, (*b)->stacktrace);
- }
-
- #if 0 /* maybe someday we'll do this by filename */
- static int cmp_lineno(struct hist *a, struct hist *b)
- {
- return (*a)->lineno - (*b)->lineno;
- }
-
- static int cmp_filename(struct hist **a, struct hist **b)
- {
- if (!(*a)->file || !(*b)->file)
- return !(*a)->file - !(*b)->file; /* with filename comes before without filename */
- return strcmp((*a)->file, (*b)->file);
- }
-
- static int cmp_diffbytes(struct hist **a, struct hist **b)
- {
- return ((*a)->bytes - (*a)->fbytes) - ((*b)->bytes - (*b)->fbytes);
- }
- #endif /* 0 */
-
- static int cmp_malloc_then_free(struct hist **a, struct hist **b)
- {
- return ((*a)->type == type_FREE) - ((*b)->type == type_FREE);
- }
-
- int (*oldcmps[])(const void *, const void *) = {
- /* cmp_filename, */
- /* cmp_lineno, */
- (int (*)(const void *, const void *))cmp_malloc_then_free,
- (int (*)(const void *, const void *))cmp_stacktrace,
- NULL
- };
- int (*cmps[])(const void *, const void *) = {
- /* cmp_filename, */
- /* cmp_lineno, */
- (int (*)(const void *, const void *))cmp_malloc_then_free,
- (int (*)(const void *, const void *))cmp_leaks,
- (int (*)(const void *, const void *))cmp_ncalls,
- (int (*)(const void *, const void *))cmp_stacktrace,
- NULL
- };
-
- extern void
- malloc_reset()
- {
- int i;
- /* can't set nhists to 0, since existing header points to them */
- for (i = 0; i < nhists; ++i) {
- hists[i].ncalls = 0;
- hists[i].nbytes = 0;
- hists[i].ncalls_freed = 0;
- hists[i].nbytes_freed = 0;
- }
- ncalls_malloc = 0;
- ncalls_free = 0;
- nbytes_malloced = 0;
- nbytes_freed = 0;
- }
-
- extern void
- malloc_info_cleanup()
- {
- stacktrace_cleanup();
- }
-
- /*
- verbose = 0: just totals
- verbose = 1: leaks with traces
- verbose = 2: all counts with traces
- */
- extern void
- malloc_info(int nonleaks_too,
- int stacktrace_print_depth)
- {
- int orig_nhists;
- int i;
- struct hist *ptrs[MAX_MALLOCS], *h;
- char thisfile[SYMBUFSIZ];
- char thisfunc[SYMBUFSIZ];
- char avgbuf[30];
- int thisline;
-
- init_from_env();
-
- LOCK_MALLOC(1);
-
- printf("\n");
- printf("%6s %10s %10s %10s %10s\n",
- " ",
- "Mallocs",
- "Frees",
- "Diff",
- "Max Diff");
- printf("-------------------------------------------------------------\n");
- printf("%6s %10d %10d %10d %10d\n",
- "Bytes:",
- nbytes_malloced_really,
- nbytes_freed_really,
- nbytes_malloced_really-nbytes_freed_really,
- max_diff_bytes);
- printf("%6s %10d %10d %10d\n",
- "Calls:",
- ncalls_malloc_really,
- ncalls_free_really,
- ncalls_malloc_really-ncalls_free_really);
- printf("Since last reset:\n");
- printf("%6s %10d %10d %10d\n",
- "Bytes:",
- nbytes_malloced,
- nbytes_freed,
- nbytes_malloced-nbytes_freed);
- printf("%6s %10d %10d %10d\n",
- "Calls:",
- ncalls_malloc,
- ncalls_free,
- ncalls_malloc-ncalls_free);
-
- /*
- if (!verbose) {
- UNLOCK_MALLOC(1);
- return;
- }
- */
- printf("%d out of %d traces used\n", nhists, MAX_MALLOCS);
- #ifdef HASH_STATS
- _malloc_history_print_stats();
- #endif
-
- printf("\n");
-
- /* printf("%15s[%4s]: %3s %6s %9s %7s %9s %7s %9s\n", */
- printf("%15s[%4s]: %3s %6s %7s %7s%6s %7s %5s %9s\n",
- "Filename",
- "Line",
- "Wha",
- "Calls",
- "Bytes",
- "Avg ",
- "FCalls",
- "FBytes",
- "Diff",
- "DiffBytes");
- printf("-------------------------------------------------------------------------------\n");
-
- orig_nhists = nhists; /* printing and the first stacktracing call malloc */
- for (i = 0; i < orig_nhists; ++i)
- ptrs[i] = hists+i;
- if (getenv("_MALLOC_DONT_SORT_BY_NCALLS"))
- multiqsort((char *)ptrs, orig_nhists,sizeof(struct hist *),oldcmps);
- else
- multiqsort((char *)ptrs, orig_nhists, sizeof(struct hist *), cmps);
-
- for (i=0; i<orig_nhists; i++) {
-
- h = ptrs[i];
-
- if (h->ncalls == 0 &&
- (h->type==type_FREE || h->ncalls_freed==0))
- continue;
-
- if (!nonleaks_too) {
- if (h->type == type_FREE)
- continue;
- if (h->ncalls == h->ncalls_freed
- && h->nbytes == h->nbytes_freed)
- continue;
- }
-
- thisfunc[0] = 0;
- thisfile[0] = 0;
- thisline = -1;
- if (h->stacktrace_depth >= 1) {
- stacktrace_get_ffl(h->stacktrace[0],
- thisfunc, thisfile, &thisline,
- SYMBUFSIZ-2, SYMBUFSIZ-2);
- }
-
- /* XXX should keep track of whether all sizes are the same */
- if (h->ncalls == 0)
- sprintf(avgbuf, " ");
- else if (h->nbytes % h->ncalls != 0)
- sprintf(avgbuf, "%d.", h->nbytes / h->ncalls);
- else
- sprintf(avgbuf, "%d ", h->nbytes / h->ncalls);
-
- if (h->type == type_MALLOC)
-
- /* printf("%15s[%4d]: %3s %6d %9d %7d %9d %7d %9d\n", */
- printf("%15s[%4d]: %3s %6d %7d %7s%6d %7d %5d %9d\n",
- thisfile,
- thisline,
- "mal",
- h->ncalls,
- h->nbytes,
- avgbuf,
- h->ncalls_freed,
- h->nbytes_freed,
- h->ncalls-h->ncalls_freed,
- h->nbytes-h->nbytes_freed);
- else
- printf("%15s[%4d]: %3s %6d %7d %7s\n",
- thisfile,
- thisline,
- "fre",
- h->ncalls,
- h->nbytes,
- avgbuf);
- /* if (verbose >= 2) */
- {
- int j;
- for (j = 0; j < h->stacktrace_depth &&
- (j < stacktrace_print_depth
- || stacktrace_print_depth < 0); j++) {
- if (!h->stacktrace[j]) {
- printf("(lost it)\n");
- break;
- }
- stacktrace_get_ffl(h->stacktrace[j],
- thisfunc, thisfile, &thisline,
- SYMBUFSIZ-2, SYMBUFSIZ);
-
- /* I don't wanna see the args, so there! */
- {
- char *p = strchr(thisfunc, '(');
- if (p) *p = '\0';
- }
- if (thisfunc[strlen(thisfunc)-1] != ')')
- strcat(thisfunc, "()");
- printf("%-20s %s:%d (%#x)\n",
- thisfunc,
- thisfile,
- thisline,
- h->stacktrace[j]);
- }
- }
- }
-
- printf("-------------------------------------------------------------------------------\n");
-
- UNLOCK_MALLOC(1);
- }
-
- /*
- Gag me-- mpc's calloc doesn't call malloc, but its free is free...
- */
- extern void *
- calloc(size_t n, size_t siz)
- {
- void *p;
- int old_fillarea;
-
- init_from_env();
-
- LOCK_MALLOC(1);
-
- old_fillarea = malloc_fillarea;
- malloc_fillarea = 0;
- p = malloc(n*siz);
- malloc_fillarea = old_fillarea;
- if (p)
- bzero(p, n*siz);
-
- UNLOCK_MALLOC(1);
- return p;
- }
-
- extern void
- cfree(void *p)
- {
- free(p);
- }
-
- /*
- Smart:
- 580 out of 600 traces used
- 13 max pileup
- 144 distinct pileups
- 309 total pileups
- 290 distinct collisions
- 536 total collisions
- Stupid:
- 581 out of 600 traces used
- 10 max pileup
- 150 distinct pileups
- 357 total pileups
- 281 distinct collisions
- 626 total collisions
-
- With crowded hash table:
- Smart:
- 581 out of 600 traces used
- 17 max pileup
- 229 distinct pileups
- 652 total pileups
- 692 distinct collisions
- 1596 total collisions
- Stupid:
- 580 out of 600 traces used
- 60 max pileup
- 227 distinct pileups
- 542 total pileups
- 1494 distinct collisions
- 3273 total collisions
- */
-
- #ifdef GETWD_PATCH
- extern void
- force_loading_of_getwd()
- {
- char buf[MAXPATHLEN+1];
- getwd(buf);
- }
- #endif /* GETWD_PATCH */
-
- extern void
- bzero(void *b, int length) /* XXX why is this not getting found by rld? */
- {
- int i;
- init_from_env();
- for (i = 0; i < length; ++i)
- ((char *)b)[i] = 0;
- }
-